/***************************************************************************
 *   Copyright (C) 2010 by Oleg Goncharov  *
 *   $EMAIL$                           *                          
 *                                                                         *
 *   This file is part of ChessVision.                                     *
 *                                                                         *
 *   ChessVision is free software; you can redistribute it and/or modify   *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#ifndef CHESSTYPES_H
#define CHESSTYPES_H

#include <opencv/cxcore.h>
#include <vector>
#include <string>

#include "vision/geomtypes.h"

namespace Chess {

typedef int Square;
typedef std::vector<Square> FigureList;
 
struct SMove {
	Square from, to;
public:
	void FromString(const std::string& str);
	void ToString(std::string& str) const;
	
	SMove() {}
	SMove(Square _from, Square _to) { 
		from = _from; to = _to;
	}
	SMove(const SMove& mv) : to(mv.to), from(mv.from) {}
	SMove(const std::string&  str) { FromString(str); }
	
	operator std::string() const { std::string st; ToString(st); return st; }
};

enum Figure {
	empty = 0,
	black = -10, bPawn = -11, bKnight = -12, bBishop = -13, bRook = -14, bQueen = -15, bKing = -16,
	white = 10, wPawn = 11, wKnight = 12, wBishop = 13, wRook = 14, wQueen = 15, wKing = 16
};

struct SBoardPosition {
	float x, y;
	float teta;	
	float h;
};


class CBoardCells {
private:
	cv::Point2f centers[64];
	cv::Point2f corners[81];
	float h;
	float halfh;
public:
	CBoardCells() {}
	CBoardCells(const SBoardPosition& bp) { Init(bp); }
	CBoardCells(const Line2f * line1, const Line2f * line2) { Init(line1, line2); }
	
	void Init(const SBoardPosition& bp);
	void Init(const Line2f * line1, const Line2f * line2);
	
	void Rotate(float angle);
	
	//TODO Add range check
	cv::Point2f Cell(int i, int j) const { return centers[i + 8*j]; }
	cv::Point2f Cell(int i) const { return centers[i]; }
	cv::Point2f Corner(int i, int j) const { return corners[i + 9*j]; }
	cv::Point2f Corner(int i) const { return corners[i]; }
	
	cv::Rect CellRect(int i, int j) const { return CellRect(i + 8*j); }
	cv::Rect CellRect(int i) const { 
		return cv::Rect((int)(centers[i].x - halfh), (int)(centers[i].y - halfh), (int) h, (int) h);
	}
	float Get_h() const { return h; }
	float getH() const { return h; }
	
	void SetROIToCell(IplImage * img, int i, int j) const {  cvSetImageROI(img, CellRect(i, j)); }
	void SetROIToCell(IplImage * img, int i) const {  cvSetImageROI(img, CellRect(i)); }
	CvMat * GetSubCell(IplImage * img, CvMat* submat, int i, int j) const { return cvGetSubRect(img, submat, CellRect(i, j)); } 
	CvMat * GetSubCell(IplImage * img, CvMat* submat, int i) const { return cvGetSubRect(img, submat, CellRect(i)); }
	CvMat GetSubRect(const cv::Mat& img, int i, int j) const { return img(CellRect(i, j)); }
	CvMat GetSubRect(const cv::Mat& img, int i) const { return img(CellRect(i)); }
	
	void DrawCells(cv::Mat& img) const;
	void DrawCells(IplImage * img) const;
};	
	
class SBoardCells {
	int h;
	float sx, sy;
	float cx, cy;
	float dx, dy;	
public:
	SBoardCells() { h = 0; sx = cx = sy = cy = dx = dy = 0.0f; }
	SBoardCells(const SBoardPosition& bp) { Init (bp); }
	SBoardCells(float x, float y, float teta, float h) { Init(x, y, teta, h); }
	
	void Init(const SBoardPosition& bp) { Init(bp.x, bp.y, bp.teta, bp.h); }
	void Init(float x, float y, float teta, float h);
	
	void Rotate(float angle);
	
	void StepX(cv::Point2f& p) const { p.x += dx; p.y += dy; }
	void StepY(cv::Point2f& p) const { p.x -= dy; p.y += dx; }
	cv::Point2f Start() const { return cv::Point2f(sx, sy); }
	cv::Point2f Cell() const { return cv::Point2f(sx, sy); }
	cv::Point2f Corner() const { return cv::Point2f(cx, cy); }
	cv::Point2f Cell(int i, int j) const { return cv::Point2f(sx + i*dx - j*dy, sy + i*dy + j*dx); }
	cv::Point2f Cell(int i) const { return Cell(i % 8, i / 8); }
	cv::Point2f Corner(int i, int j) const { return cv::Point2f(cx + i*dx - j*dy, cy + i*dy + j*dx); }
	cv::Point2f Corner(int i) const { return Corner(i % 8, i / 8); }
	
	cv::Rect CellRect(int i, int j) const { 
		//TODO (int) -> cvRound
		int x = (int) (sx + i*dx - j*dy);
		int y = (int) (sy + i*dy + j*dx);
		int halfh = (int) (h/2);
		return cv::Rect(x - halfh, y - halfh, h, h);
	}
	cv::Rect CellRect(int i) { return CellRect(i % 8, i / 8); }

	int Get_h() const { return h; }
	
	void SetROIToCell(IplImage * img, int i, int j) const {  cvSetImageROI(img, CellRect(i, j)); }
	void SetROIToCell(IplImage * img, int i) const { SetROIToCell(img, i % 8, i / 8); }
	CvMat * GetSubCell(IplImage * img, CvMat* submat, int i, int j) const { return cvGetSubRect(img, submat, CellRect(i, j)); } 
	CvMat * GetSubCell(IplImage * img, CvMat* submat, int i) const { return GetSubCell(img, submat, i % 8, i / 8); }
	
	void DrawCells(IplImage * img) const;
};

class CPosition {
public:
	
protected:
	Figure position[64];
public:
	void Clear();
	CPosition(bool place_figures = false);
	
	Figure& Cell(Square i) { return position[i]; }
	Figure& Cell(int i, int j) { return position[8*j + i]; }
	Figure Get(Square i) const { return position[i]; }
	Figure Get(int i, int j) const { return position[8*j + i]; }
	bool IsEmpty(Square i) const { return abs(position[i]) < 10; }
	bool IsBlack(Square i) const { return position[i] <= -10; }
	bool IsWhite(Square i) const { return position[i] >= 10; }
	
	bool CheckCastle(const SMove& mv, SMove& rook_move) const;
	void Move(const SMove& mv);
	
	int Difference(const CPosition& prev, FigureList& disappear, FigureList& appear, FigureList& change) const;
	int Difference(const CPosition& prev) const;
	 
	void InitialPosition();
	void RotateHalfPI();
	void RotatePI();
	void DrawPosition(IplImage * img, const CBoardCells& cells) const;
	void DrawPosition(cv::Mat& img, const CBoardCells& cells) const;
};

}

#endif
